import wx
from wx import glcanvas
from numpy import array
from numpy.linalg import norm

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

SELECT_BUFFER_SIZE = 1024

# define new canvaspick event
class CanvasPickEvent(wx.PyCommandEvent):
	def __init__(self, evtType, id):
		wx.PyCommandEvent.__init__(self, evtType, id)
		self.elementid = None

	def GetId(self):
		return self.elementid


EVT_CANVAS_PICK_type = wx.NewEventType()
EVT_CANVAS_PICK = wx.PyEventBinder(EVT_CANVAS_PICK_type, 1)

class GLCanvasBase(glcanvas.GLCanvas):
	def __init__(self, parent):
		glcanvas.GLCanvas.__init__(self, parent, wx.ID_ANY)
		self.init = False
		self.size = None

		self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground)
		self.Bind(wx.EVT_SIZE, self.OnSize)
		self.Bind(wx.EVT_PAINT, self.OnPaint)

	def OnEraseBackground(self, evt):
		pass #do nothing, prevent flicker

	def OnSize(self, evt):
		size = self.size = self.GetClientSize()
		if self.GetContext():
			self.SetCurrent()
			glViewport(0, 0 ,size.width, size.height)
			aspect = float(size.width)/float(size.height)
			glMatrixMode(GL_PROJECTION)
			glLoadIdentity()
			#gluPerspective(45.0,aspect,0.1,100.0)
			gluOrtho2D(0.0,size.width,0.0,size.height)

		evt.Skip() #propagate to standard size handler

	def OnPaint(self, evt):
		dc = wx.PaintDC(self)
		self.SetCurrent()
		if not self.init:
			self.InitGL()
			self.init = True

		self.OnDraw(select = False)

class RoadCanvas(GLCanvasBase):
	def __init__(self, parent, model=None):
		GLCanvasBase.__init__(self, parent)
		self.views = []
		self.SetModel(model)

		self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
		self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)

		self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
		self.Bind(wx.EVT_RIGHT_UP, self.OnRightUp)

		self.Bind(wx.EVT_MOTION, self.OnMotion)
		self.Bind(wx.EVT_MOUSEWHEEL, self.OnWheel)

	def SetModel(self, view):
		if not view:
			return

		if not len(self.views):
			self.SetTransform(view.bbox)
		self.views.append(view)
		
	def SetTransform(self, bbox, viewsize = 400.0):
		leftdown, rightup = bbox
		leftdown = array(leftdown,dtype="float")
		rightup = array(rightup,dtype="float")
		self.center = (leftdown+rightup)/2.0
		self.scale = viewsize / norm(leftdown-rightup)
	
	def InitGL(self):
		glClearColor(1.0,1.0,1.0,0.0)
		glShadeModel(GL_SMOOTH)
		
		glClearDepth(1.0)
		glDepthFunc(GL_LESS)
		#glEnable(GL_DEPTH_TEST)

		size = self.size = self.GetClientSize()
		#aspect = float(size.width)/float(size.height)
		glViewport(0, 0 ,size.width, size.height)
		glMatrixMode(GL_PROJECTION)
		glLoadIdentity()
		#gluPerspective(45.0,aspect,0.1,100.0)
		gluOrtho2D(0.0,size.width,0.0,size.height)

	def OnDraw(self, select=False):
		#print "OnDraw"
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

		glMatrixMode(GL_MODELVIEW)
		glLoadIdentity()
		glScalef(self.scale, self.scale, self.scale)
		glTranslatef(-self.center[0], -self.center[1], 0.0)

		if not len(self.views):
			if select:
				glInitNames()
				glPushName(0)

			if select:
				glLoadName(1)
			glBegin(GL_QUADS)
			glColor3f(0.0, 0.0, 1.0)
			glVertex3f(10,10,0)
			glVertex3f(-10,10,0)
			glVertex3f(-10,-10,0)
			glVertex3f(10,-10,0)
			glEnd()
		
			if select:
				glLoadName(2)
			glTranslatef(50,50,0)
			glBegin(GL_QUADS)
			glColor3f(1.0, 0.0, 0.0)
			glVertex3f(10,10,0)
			glVertex3f(-10,10,0)
			glVertex3f(-10,-10,0)
			glVertex3f(10,-10,0)
			glEnd()

			if select:
				glLoadName(3)
			glLineWidth(5.75)
			glBegin(GL_LINE_STRIP)
			glColor3f(1.0, 0.0, 0.0)
			glVertex3f(100,100,0)
			glColor3f(0.0, 1.0, 0.0)
			glVertex3f(-100,-100,0)
			glColor3f(0.0, 0.0, 1.0)
			glVertex3f(-100,100,0)
			glColor3f(1.0, 0.0, 0.0)
			glVertex3f(100,-100,0)
			glEnd()
		else:
			for view in self.views:
				view.Draw(select=select)
		
		if not select:
			self.SwapBuffers()

	def OnLeftDown(self, evt):
		self.CaptureMouse()
		self.lastx, self.lasty = evt.GetPosition() 

	def OnLeftUp(self, evt):
		self.ReleaseMouse()

	def OnMotion(self, evt):
		if evt.Dragging() and evt.LeftIsDown():
			x,y = evt.GetPosition()
			deltax = x - self.lastx
			deltay = self.lasty -y
			self.center[0] -= deltax/self.scale
			self.center[1] -= deltay/self.scale
			self.lastx = x
			self.lasty = y
			self.Refresh(False)

	def OnRightDown(self, evt):
		x, y = evt.GetPosition()
		y = self.GetClientSize().height - y

		glSelectBuffer(SELECT_BUFFER_SIZE)
		
		glMatrixMode(GL_PROJECTION)
		glPushMatrix()

		glRenderMode(GL_SELECT)
		glLoadIdentity()
		gluOrtho2D(x,x+1,y,y+1)

		self.OnDraw(select = True)
		buffer = glRenderMode(GL_RENDER)

		glMatrixMode(GL_PROJECTION)
		glPopMatrix()

		for hit_record in buffer:
			print tuple(hit_record)
		
		#send the canvas pick event
		evt = CanvasPickEvent(EVT_CANVAS_PICK_type, self.GetId())
		evt.elementid = buffer
		self.GetEventHandler().ProcessEvent(evt)

	def OnRightUp(self, evt):
		pass

	def OnWheel(self, evt):
		if evt.GetWheelRotation() > 0:
			self.scale *= 1.2
		else:
			self.scale /= 1.2

		self.Refresh(False)


if __name__ == "__main__":
	import roadview
	view = roadview.RoadViewBase()
	app = wx.App(False)
	frame = wx.Frame(None)
	canvas = RoadCanvas(frame, view)
	frame.Show()
	app.MainLoop()

